package main

import (
	jdb "gitee.com/withu2018/gowebtools/db"
	jweb "gitee.com/withu2018/gowebtools/web"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"github.com/antlinker/go-dirtyfilter"
	"github.com/antlinker/go-dirtyfilter/store"
	"github.com/gin-gonic/gin"
	uuid "github.com/satori/go.uuid"
	"io/ioutil"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"
)

var Appid = "net.piratepig"

func md5str(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

//checkParam 检查参数是否有效
func checkParam(c *gin.Context, key string) bool {
	if c.PostForm(key) != "" {
		return true
	}
	return false
}

type replyMsg struct {
	Code   int         `json:"code,omitempty"`
	Cbk    string      `json:"cbk,omitempty"`
	Status string      `json:"status,omitempty"`
	Msg    string      `json:"msg,omitempty"`
	Data1  interface{} `json:"data1,omitempty"`
	Data2  interface{} `json:"data2,omitempty"`
	Data3  interface{} `json:"data3,omitempty"`
	Data4  interface{} `json:"data4,omitempty"`
	Data5  interface{} `json:"data5,omitempty"`
}

type userInfo struct {
	Uid    string `json:"uid"`
	Score  int64  `json:"score"`
	Nc     string `json:"nc"`
	LastGk string `json:"lastgk"`
}

type Scores []userInfo

func getSocreList(gk string) Scores {
	var scoreList Scores
	jdb.GetEach(Appid, "score"+gk, func(k, v []byte) error {
		socre64, _ := strconv.ParseInt(string(v), 10, 64)

		scoreList = append(scoreList, userInfo{
			Uid:    string(k),
			Score:  socre64,
			Nc:     "",
			LastGk: "0",
		})
		return nil
	})
	return scoreList
}

//获取排名
func getPm(uid string, gk string) (int64, Scores) {
	var scoreList Scores
	var meRank int64
	meRank = 0
	scoreList = getSocreList(gk)
	sort.Sort(scoreList)
	for i := range scoreList {
		if scoreList[i].Uid == uid {
			meRank = int64(i + 1)
			break
		}
	}
	return meRank, scoreList
}

func (s Scores) Len() int           { return len(s) }
func (s Scores) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s Scores) Less(i, j int) bool { return s[i].Score > s[j].Score }

func main() {

	fileContent, err := ioutil.ReadFile("mgc.txt")
	if err != nil {
		panic("敏感词文件读取失败")
	}

	memStore, err := store.NewMemoryStore(store.MemoryConfig{
		Delim:      ',',
		DataSource: strings.Split(string(fileContent), ","),
	})
	if err != nil {
		log.Println("DirtyManager Load Err")
		panic(err)
	}
	filterManage := filter.NewDirtyManager(memStore)

	log.Println(md5str("中文"))
	g := jweb.GetGin(true)

	g.Use(func(c *gin.Context) {
		if c.Request.Method != http.MethodPost {
			c.Next()
			return
		}

		const needCheckSec = true
		_ = c.Request.ParseForm()
		//log.Println(c.Request.PostForm)
		if needCheckSec && ("" == c.PostForm("sec") || "" == c.PostForm("secc")) {
			c.JSON(200, &replyMsg{
				Code: 503, Msg: "参数不完整",
			})
			c.Abort()
			return
		}

		var s []string
		var postValue []string
		for k, _ := range c.Request.PostForm {
			log.Println(k)
			s = append(s, k)
		}
		//log.Println("sec:",s)
		sort.Strings(s)
		//log.Println("sec:",s)
		for _, v := range s {
			log.Print(v)
			if v != "sec" && v != "secc" {
				postValue = append(postValue, c.PostForm(v))
			}
		}
		sec := c.PostForm("sec")
		secc := c.PostForm("secc")
		log.Println("postValue Strings", strings.Join(postValue, ""))
		calcSec := md5str(strings.Join(postValue, ""))
		calcSecc := md5str(calcSec + "/goBackend2020")
		log.Println("sec:", sec, calcSec)
		log.Println("secc:", secc, calcSecc)
		if sec == calcSec && secc == calcSecc {
			c.Next()
		} else {
			c.JSON(200, &replyMsg{
				Code: 503, Msg: "参数校验错误",
			})
			c.Abort()
		}
	})
	//新用户
	//参数POST：
	// 仅返回一个新的guid
	g.POST("/user-new", func(c *gin.Context) {
		c.JSON(200, &replyMsg{Code: 200, Msg: uuid.NewV4().String(),})
	})

	// 获取用户数量
	g.POST("/user-count", func(c *gin.Context) {
		c.JSON(200, &replyMsg{
			Code: 200,
			Msg:  strconv.Itoa(len(jdb.GetSlice(Appid, "user", ""))),
		})
	})

	g.POST("/user-info", func(c *gin.Context) {
		if c.PostForm("uid") != "" {
			userinfo := jdb.Get(Appid, "user", c.PostForm("uid"))
			if userinfo != "" {
				var infoMap map[string]interface{}
				err := json.Unmarshal([]byte(userinfo), &infoMap)
				if err != nil {
					c.JSON(200, &replyMsg{
						Code: 503, Msg: "err",
					})

					return
				}
				c.JSON(200, &replyMsg{
					Code: 200, Msg: "ok",
					Data1: infoMap,
				})
			}
		} else {
			c.JSON(200, &replyMsg{
				Code: 503,
				Msg:  "err",
			})
		}
	})

	//更新用户信息
	//uid:唯一标识GUID,info:信息，建议使用json字符串(包含 nc:昵称,v:客户端版本和设备信息(1.0-iphone),lang:语言)
	g.POST("/user-update", func(c *gin.Context) {
		if c.PostForm("uid") != "" && c.PostForm("info") != "" {
			info := c.PostForm("info")
			var infoMap map[string]interface{}
			err := json.Unmarshal([]byte(info), &infoMap)
			if err != nil {
				c.JSON(200, &replyMsg{
					Code: 503,
					Msg:  "info nc err",
				})
				return
			}
			nc, ok := infoMap["nc"].(string)
			uid := c.PostForm("uid")
			if !ok {
				c.JSON(200, &replyMsg{
					Code: 503,
					Msg:  "nc empty",
				})
				return
			}

			hasNcUid := jdb.Get(Appid, "userNcKey", nc)
			if hasNcUid != "" && hasNcUid != uid {
				//	如果已有的昵称对应的uid不为空，且uid与提交的用户不同，则表示新用户想要取名，且已经存在
				c.JSON(200, &replyMsg{
					Code: 503,
					Msg:  "nc had",
				})
				return
			}

			mgc, err := filterManage.Filter().Filter(nc, '*', '@', '#', ' ', '-', '+', '/')

			if !(mgc == nil && err == nil) {
				log.Println("发现敏感词", mgc, err)
			} else {
				log.Println("无敏感词")
			}

			if !(mgc == nil && err == nil) {
				log.Println(mgc, err)
				c.JSON(200, &replyMsg{
					Code: 503,
					Msg:  "nc filter",
				})
				return
			}

			//在新昵称设置前，删除原来的昵称，否则昵称就无法被释放了
			oldNc := jdb.Get(Appid, "userNc", uid)
			if oldNc != "" {
				_ = jdb.DeleteKey(Appid, "userNcKey", oldNc)
			}

			_ = jdb.Put(Appid, "userNc", uid, nc)
			_ = jdb.Put(Appid, "userNcKey", nc, uid)
			_ = jdb.Put(Appid, "user", c.PostForm("uid"), info)
			c.JSON(200, &replyMsg{
				Code: 200,
				Msg:  "ok",
			})
			c.Abort()
		} else {
			c.JSON(200, &replyMsg{
				Code: 503,
				Msg:  "err",
			})
			c.Abort()
		}
	})

	//更新得分
	//uid:用户GUID,gk:关卡 0总分 >小0关卡,score:分数
	//返回本关的排名
	g.POST("/score-update", func(c *gin.Context) {
		uid := c.PostForm("uid")
		gk := c.PostForm("gk")
		score := c.PostForm("score")
		if uid != "" && gk != "" && score != "" {

			gkInt, errgk := strconv.ParseInt(gk, 10, 32)
			if errgk != nil {
				c.JSON(200, &replyMsg{
					Code: 503,
					Msg:  "gk error",
				})
				return
			}

			lastgk := jdb.Get(Appid, "lastgk", uid)
			lastgkInt, errlastgk := strconv.ParseInt(lastgk, 10, 32)
			if errlastgk != nil {
				lastgkInt = 0
			}

			if lastgkInt < gkInt {
				jdb.Put(Appid, "lastgk", uid, strconv.FormatInt(gkInt, 10))
			}

			jdb.Put(Appid, "score"+gk, uid, score)
			pm, _ := getPm(uid, gk)
			c.JSON(200, &replyMsg{
				Code: 200,
				Msg:  strconv.FormatInt(pm, 10),
			})
		} else {
			c.JSON(200, &replyMsg{
				Code: 503,
				Msg:  "err",
			})
		}
	})

	//获取top200
	//uid:用户id gk:关卡
	g.POST("/score-top", func(c *gin.Context) {
		uid := c.PostForm("uid")
		gk := c.PostForm("gk")
		if uid != "" && gk != "" {
			mePm, list := getPm(uid, gk)
			if len(list) > 200 {
				list = list[:200]
			}

			for i := range list {
				list[i].Nc = jdb.Get(Appid, "userNc", list[i].Uid)
				lastgk := jdb.Get(Appid, "lastgk", list[i].Uid)
				if lastgk == "" {
					lastgk = "0"
				}
				list[i].LastGk = lastgk
			}
			c.JSON(200, &replyMsg{
				Code:  200,
				Msg:   "ok",
				Data1: strconv.FormatInt(mePm, 10),
				Data2: list,
			})
		} else {
			c.JSON(200, &replyMsg{
				Code: 503,
				Msg:  "err",
			})
		}
	})

	g.Static("/public", "public")
	g.GET("/", func(c *gin.Context) {
		c.Redirect(http.StatusMovedPermanently, "/public")
	})
	//go logClients()
	jweb.StartWS("/ws", g)
	g.Run(":9600")
}
